package com.universe;

import com.Properties;
import com.dmxrow.MainWindow;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * @author sad
 */
public class Universe extends JPanel {

    private byte[] data;
    private final BufferedImage image;
    private final int[] rgbIndexes = new int[]{0, 1, 2};
    private final int[] bgrIndexes = new int[]{2, 1, 0};
    private Wheel wheel;
    private int speed;
    private Timer timer;
    private int angle = 0;
    private int universeIndex;
    private int wheelCommandOffset = 0;
    private int[] pixelMapping = new int[13 * 13];

    private MainWindow window;

    public Universe() {
        setBorder(BorderFactory.createEmptyBorder());
        image = new BufferedImage(13, 13, BufferedImage.TYPE_3BYTE_BGR);
        Dimension d = new Dimension(13 * 5, 13 * 5);
        setPreferredSize(d);
        setMinimumSize(d);
        setMaximumSize(d);
        addMouseMotionListener(new MouseMotionAdapter() {

            @Override
            public void mouseMoved(MouseEvent e) {
                float d = getWidth() / 13.0f;
                int x = (int) (e.getX() / d);
                int y = (int) (e.getY() / d);
                String val = "x=" + x + "; y=" + y;
                int ty = 12 - y;
                if (ty % 2 == 1) {
                    int tx = 13 - x - 1;
                    int offset = ty * 13 + tx;
                    val += " Offset=" + offset + " ByteOffset=" + offset * 3 + "  OffsetLine=" + (y * 13 + x);
                } else {
                    int offset = ty * 13 + x;
                    val += " Offset=" + offset + " ByteOffset=" + offset * 3 + "  OffsetLine=" + (y * 13 + x);
                }
                window.setTitle(val);
            }

        });
    }

    public void setWindow(MainWindow window) {
        this.window = window;
    }

    public void setUniverseIndex(int universeIndex) {
        this.universeIndex = universeIndex;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    private void stopWheelTimer() {
        if (timer != null) {
            timer.stop();
            timer = null;
        }
    }

    public void startWheelTimer() {
        stopWheelTimer();
        timer = new Timer(50, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (wheel == null) {
                    stopWheelTimer();
                    return;
                }

                processWheelByTimer();
            }
        });

        timer.start();
    }

    private void processWheelByTimer() {
        if (speed == 0) {
            return;
        }

        if (speed > 0) {
            angle += 1;
        } else {
            angle -= 1;
        }
        repaint();
    }

    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
        //g.drawImage(image, 0, 0, this);
        if (wheel != null) {
            g.setXORMode(Color.WHITE);
            g.setColor(Color.BLACK);
            g.drawOval(10, 10, getWidth() - 20, getHeight() - 20);
            drawRay(g, getWidth() / 2, getHeight() / 2, getWidth() / 2 - 10, angle);
            drawRay(g, getWidth() / 2, getHeight() / 2, getWidth() / 2 - 10, angle + 90);
            drawRay(g, getWidth() / 2, getHeight() / 2, getWidth() / 2 - 10, angle + 180);
            drawRay(g, getWidth() / 2, getHeight() / 2, getWidth() / 2 - 10, angle + 270);
        }
    }

    private void drawRay(Graphics g, int x, int y, int length, int angle) {
        int tx = (int) (Math.cos(Math.toRadians(angle)) * length + x);
        int ty = (int) (Math.sin(Math.toRadians(angle)) * length + y);
        g.drawLine(x, y, tx, ty);
    }

    public byte[] getData() {
        return data;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void createPixelMapping() {
        int index = 0;
        for (int j = 12; j >= 0; j--) {
            for (int i = 0; i < 13; i++, index++) {
                int y = j;
                if (j % 2 == 1) {
                    int x = 13 - i - 1;
                    pixelMapping[index] = y * 13 + x;
                } else {
                    int x = i;
                    pixelMapping[index] = y * 13 + x;
                }
            }
        }
    }

    public void setData(byte[] data) {
        this.data = data;
        Graphics g = image.getGraphics();
        g.setColor(Color.green);
        g.fillRect(0, 0, image.getWidth(), image.getHeight());
        g.dispose();
        WritableRaster raster = image.getRaster();
        int[] pixelData = new int[3];
        int index = 0;
        int[] colorOffset = Properties.imageFormat == Properties.ImageFormat.BGR ? bgrIndexes : rgbIndexes;
        int sample = 0;
        for (int j = 0; j < 13; j++) {
            for (int i = 0; i < 13; i++, index++, sample += 3) {
                pixelData[0] = data[sample + colorOffset[0]];
                pixelData[1] = data[sample + colorOffset[1]];
                pixelData[2] = data[sample + colorOffset[2]];
                int position = pixelMapping[index];
                if (position > -1) {
                    int y = position / 13;
                    int x = position - y * 13;
                    raster.setPixel(x, y, pixelData);
                }
            }
        }
        if (wheel != null) {
            int tspeedl = data[wheelCommandOffset * 3];
            int tspeedr = data[wheelCommandOffset * 3 + 1];
            if (tspeedl != 0 || tspeedr != 0) {
                int newSpeed = tspeedl == 0 ? tspeedr : tspeedl * -1;
                if (speed != newSpeed) {
                    System.out.println("Wheel " + newSpeed);
                    speed = newSpeed;
                    startWheelTimer();
                }
            } else {
                speed = 0;
                stopWheelTimer();
            }
        }

        repaint();
    }

    int[][] wheelmask = new int[][]{
        {1, 1, 0, 0, 0, 1, 1},
        {1, 0, 0, 0, 0, 0, 1},
        {0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0},
        {1, 0, 0, 0, 0, 0, 1},
        {1, 1, 0, 0, 0, 1, 1},};

    public void recalculateMasks() {
        int y = universeIndex / 6;
        int x = universeIndex - y * 6;
        x *= 13;
        y *= 13;
        for (Wheel w : Properties.profile.getWheels().getWheels()) {
            int wX = w.getScreenX();
            int wY = w.getScreenY();
            wX -= 3;
            wY -= 3;
            copyWheelShadowToMask(wX, wY, pixelMapping, x, y);
        }
        //printMapping(pixelMapping);
        pixelMapping = collapseShadowMask(pixelMapping);
        int wheelOffset = 0;
        for (int value : pixelMapping) {
            if (value != -1) {
                wheelOffset++;
            }
        }

        wheelCommandOffset = wheelOffset;
    }

/*
    private void printMapping(int[] mapping) {
        int sample = 0;
        for (int j = 0; j < 13; j++) {
            for (int i = 0; i < 13; i++, sample++) {
                System.out.print(String.format("%3d ", mapping[sample]));
            }
            System.out.println();
        }
        System.out.println();
    }
*/

    public int[] collapseShadowMask(int[] mask) {
        int[] newMask = mask;
        //Arrays.fill(newMask, -1);
        int index = 0;
        for (int i = 0; i < mask.length; i++) {
            if (!(mask[i] == -1)) {
                newMask[index] = mask[i];
                index++;
            }
        }
        for (; index < newMask.length; index++) {
            newMask[index] = -1;
        }

        return newMask;
    }

    private void copyWheelShadowToMask(int wheelX, int wheelY, int[] mask, int universeX, int universeY) {
        int universeX2 = universeX + 13;
        int universeY2 = universeY + 13;
        for (int j = 0; j < 7; j++) {
            for (int i = 0; i < 7; i++) {
                int tx = wheelX + i;
                int ty = wheelY + j;
                if (tx >= universeX && tx < universeX2 && ty >= universeY && ty < universeY2) {
                    tx -= universeX;
                    ty -= universeY;
                    if (wheelmask[i][j] == 0) {
                        if (ty % 2 == 1) {
                            mask[ (12 - ty) * 13 + (12 - tx)] = -1;
                        } else {
                            mask[ (12 - ty) * 13 + tx] = -1;
                        }
                    }
                }
            }
        }
    }

    /* public int[] collapseShadowMask(int[] mask) {
     int[]newMask=mask;
     //Arrays.fill(newMask, -1);
     int index = 0;
     for (int i = 0; i < mask.length; i++) {
     if (!(mask[i] == -1)) {
     newMask[index] = mask[i];
     index++;
     }
     }
     for(;index<newMask.length;index++){
     newMask[index]=-1;
     }

     wheelCommandOffset = index * 3;
     return newMask;
     }*/
}
